窗口应用程序的事件驱动、消息响应、映射、自定义

控制台程序的流程是由代码编写的顺序来控制,是过程驱动,而窗口程序却是事件驱动。

在Win32的窗口程序中,采用switch_case结构进行消息处理,在MFC中通过消息映射的方法,将消息映射到各个处理函数。

窗口是位于屏幕中的一个矩形区域,它用于接收用户的输入,然后以文本或图形的形式显示输出。 窗体(控件)类就要是对输入输出界面的封装。

1 Windows的编程模型与MS-DOS编程模型之比较

第一,用C语言编写基于MS-DOS的应用程序时,唯一绝对需要的是一个名为main的函数。而当Windows操作系统启动一个程序时,调用的是WinMain函数。

第二,许多MS-DOS程序直接写显存和打印机接口。这种技术的不利之处是对每一种设备需要其支持的驱动程序软件。Windows引入了一个名为图形设备接口(GDI)的抽象化外层,所以用户不必知道有关系统设备的类型。Windows程序不是寻址硬件,而是调用GDI函数。

第三,要在MS-DOS环境下进行数据驱动编程,必须或者为把数据编码成为初始化常量或者提供独立的数据文件让程序来读。进行Windows编程时,使用大量已经确立的格式在资源文件中存储数据。

第四,在MS-DOS环境下一个程序的所有对象模块在建立过程中是静态连接的。Windows允许动态链接,这意味着特别创建的库可以在运行时加载和链接。多个应用程序可以共享动态链接库(DLLs),它节省内存和磁盘空间。动态链接增加了程序的模块性。

2 事件驱动、消息响应

所谓消息就是用于描述某个事件发生的信息,而事件是对于Windows的某种操作,消息相当于事件的数据化,一般通过一个结构体来描述。

事件和消息密切相关,事件是因,消息是果,事件产生消息,消息对应事件。所谓消息的响应,其实质就是事件的响应。

消息驱动是Windows应用程序的核心,所有的外部响应(如键盘、鼠标和计时器等)都被Windows先拦截,转换成消息后再发送到应用程序中的目标对象,应用程序根据消息的具体内容进行处理。

消息是操作系统将事件传递给用户程序的数据格式,由消息类型和相关数据组成,如当鼠标单击了一个按钮时,应用程序就会接到一个消息,消息的类型是WM_COMMAND,而数据是一个按钮的ID,应用程序根据ID可以判断用户单击的是哪个按钮。

消息不仅可由Windows发出,它也可由应用程序本身或其它程序产生。Windows为每一个应用程序都维护一个或多个消息队列,发送到每个程序窗口的消息都排成一个队列。

图片描述(最多50字)

Windows本身维护一个系统消息队列,对于每一个正在运行的Windows应用程序,系统都会为其建立一个消息队列,用于存放该应用程序可能创建的各种消息。消息队列是一个先进先出的缓冲区,通常是一个某种变量类型的数组。消息队列里的每一个元素都是一条消息。操作系统将生成的每个消息按先后顺序放进队列里。

一般来说,应用程序都包含一段消息循环代码,用于从消息队列中检索这些消息。应用程序总是先取走第一条消息。消息取走后,第二条消息成为第一条,剩余的消息依次往前推。应用程序取得消息后,便能够知道用户的操作和程序状态的变化,并把它们分发到相应的函数中进行处理。

图片描述(最多50字)

消息响应是用户通过编码实现的,这也是Windows程序的主要代码。

在消息响应代码中,很可能又要调用操作系统提供的API函数,以便完成特定的功能。如果用户收到窗口的WM_CLOSE消息,可以调用API函数DestroyWindow来关闭该窗口,或是用MessageBox函数来提示用户是否真的要关闭窗口。

Windows程序设计中,在应用程序中要完成某个功能,都是以函数调用的形式实现的。同样,应用程序也是以函数调用的方式来通知操作系统执行相应的功能。Windows操作系统所能够完成的每一个特殊功能通常都有一个函数与其对应。也就是说,操作系统把它所能够完成的功能以函数的形式提供给应用程序使用。应用程序对这些函数的调用就叫做系统调用。这些函数的集合就是Windows操作系统提供给应用程序编程的接口(Application Programming Interface),简称Windows API。

Windows编程的主要代码都集中在对消息的处理上,或者说编写消息响应(处理)函数是进行Windows编程的主要工作。

3 消息类别

当用户需要完成某种功能时会调用操作系统的某种支持,然后操作系统将用户的需要包装成消息,并投递到消息队列中,最后应用程序从消息队列中取得消息并进行响应。

在应用程序中,用户所有的操作都是通过消息机制(Message)来传递给操作系统的。操作系统将每个事件都包装成一个称为消息的结构体MSG来传递给应用程序。例如用户在某个程序活动时按了一下键盘,操作系统马上能感知到这一事件,并且能够知道用户按下的是哪一个键。操作系统并不决定对这一事件如何作出反应,而是将这一事件转交给应用程序。由应用程序决定如何对这一事件作出反应,对事件作出反应的过程就是消息响应。

消息用一个结构体来描述,区别消息一般是对结构体中的主消息message、附加参数wParam和lParam3个字段进行判断。

typedef struct tagMSG {

HWND hWnd; // 消息来源的窗口句柄

UINT message; // 消息ID

WPARAM wParam; // 消息附加参数1(附加信息,16位)

LPARAM lParam; // 消息附加参数2(附加信息,32位)

DWORD time; // 消息发送时间

POINT pt; // 消息发送时鼠标的屏幕坐标

} MSG;

在MFC中,消息分为窗口消息,命令消息和控件消息三种类型。

图片描述(最多50字)

许多情况下,Windows编程也就是编写消息处理函数。Windows程序设计中最常用的一些消息有:

1) 键盘消息

2) 鼠标消息

3) 窗口消息

4) 焦点消息

5) 定时器消息

6) 命令

4 基于资源的程序设计

Windows应用程序常常包含众多图形元素,例如光标、菜单、工具栏、位图、对话框等,在Windows环境下,每一个这样的元素都作为一种可以装入应用程序的资源来存放。这些资源就像C++程序中的常量一样,可以被编辑、修改,也可以被其他应用程序所共享。VisualC++中就提供这样的编辑器,可“所见即所得”地对这些不同类型的资源进行设计、编辑等。

Windows程序中产生的任何资源(要占用某一块或大或小的内存),如图标、光标、窗口和应用程序的实例(已加载到内存运行中的程序)等,都要将它们放入相应的内存,并为这些内存指定一个唯一的标识号。这个标识号即是该资源的句柄。

操作系统要管理和操作这些资源,都是通过句柄来找到对应的资源的。一般来说,按资源的类型,可以将句柄细分成图标句柄(HICON)、光标句柄(HCURSOR)、窗口句柄(HWND)、应用程序实例句柄(HINSTANCE)等各种类型的句柄。例如,操作系统给每一个窗口指定的一个唯一的标识号即窗口句柄。

在Visual C++中所有诸如对话框、菜单等都称为资源。资源由资源编辑器进行管理。资源编辑器存在于Workspace工作区中。它提供了一个所见即所得的菜单编辑器和一个强大的对话框图形编辑器。另外,它还包含了编辑图标(Icon)、位图(BMP)和字符串(String)等资源的工具。

每一个工程通常有一个文本格式的资源脚本(RC)文件来描述工程的菜单、对话框、字符串等资源。RC文件也可以通过#include语句(包含库文件语句)从其他子目录中引进资源。这些资源包括位图、图标以及所有Visual C++程序共用的资源。用户可以通过直接编辑文本形式的RC文件来编辑各种资源。但是一般来说,推荐使用通过资源编辑器来编辑资源。

Visual C++的资源编译器从资源编辑器中读取ASCII资源脚本文件,并且向链接程序提供一个二进制RES文件。

链接器从C/C++编译器和资源编译器产生的目标文件OBJ 文件和资源文件RES 文件中读取信息,连同Windows 运行库和MFC 库,访问LIB 文件,最后生成工程的EXE 文件。链接时间的长短取决于对源文件改动的大小。

5 在类中实现事件

在许多语言中都有对事件的定义,事件的作用是可以在类外实现一个事件,然后在类中调用这个事件。这样在设计类时就可以不必实现某些功能,而这些功能可以交给外部函数来处理,这样更增加了程序的灵活性。

在C++语言中实现事件可以使用函数回调的方法,而函数回调就是使用函数指针来实现的。注意这里针对的是普通的函数,不包括完全依赖于不同语法和语义规则的类成员函数。声明函数指针回调函数是一个程序员不能显式调用的函数,通过将回调函数的地址传给调用者从而实现调用。要实现回调,必须首先定义函数指针。尽管定义的语法有点不可思议,但如果你熟悉函数声明的一般方法,便会发现函数指针的声明与函数声明非常类似。

6 MFC编程与消息映射

无论是API还是MFC编程,程序员最关心的内容有三个:

1) 程序入口;

2) 窗口、资源等的创建和使用;

3) 键盘、鼠标等所产生的事件或消息的接收和处理;

从MFC的角度看一个对话框是由对话框资源和对话框类共同生成的。

1) 创建对话框模板资源,并向对话框模板资源添加控件;

2) 生成对话框类,并添加与控件关联的成员变量和消息处理函数(类的成员函数包括普通成员函数和消息响应函数);

3) 在程序中显示对话框并访问与控件关联的成员变量。

Win32工程的消息处理机制是每个窗口都关联一个全局的消息回调函数,在消息回调函数中,通过对消息类型的判别来响应不同的消息。MFC的消息处理不是使用一个全局的回调函数,而是创建一个与窗口关联的派生类,用于消息关联的成员函数来接收和处理窗口的消息。

MFC采用消息映射(Message Map)机制取代C/C++语言中的switch-case结构来处理消息。

MFC消息映射机制包括一组消息映射宏。一条消息映射宏把一个Windows消息和其消息处理函数联结起来。

当窗口接收到消息时,会到消息映射表中查找该消息对应的消息处理函数,然后由消息处理函数进行相应的处理。SDK编程时需要在窗口过程中一一判断消息值进行相应的处理,相比之下MFC的消息映射机制要方便好用的多。

在Win32的消息处理机制中,每一个消息类型发送到窗口时,可能在wParam和lParam中携带一些相关的数据。例如,在WM_COMMAND的消息中,在wParam中记录了单击的按钮或者菜单项等ID号码。而在WM_LBUTTONDOWN消息中,在lParam中记录了单击界面的(x,y)坐标等。Win32解析这些附带的数据比较繁琐,而MFC的消息映射机制,一般不需要开发者解析这些复杂的数据,因为消息映射函数会自动在参数中将解析好的数据传递过来。如OnMouseMove(UINT nFlags, CPoint point)函数。

除了一些没有基类的类或CObject的直接派生类外,其他的类都可以自动生成消息映射表。

在类(如Records)声明中用DECLARE_MESSAGE_MAP()宏来声明使用消息映射::

//{{AFX_MSG(Records)

afx_msg void OnButton18();

……

//}}AFX_MSG

DECLARE_MESSAGE_MAP()

用BEGIN_MESSAGE_MAP()和END_MESSAGE_MAP()宏来定义消息映射:

BEGIN_MESSAGE_MAP(Records, CDialog)

//{{AFX_MSG_MAP(Records)

ON_BN_CLICKED(IDC_BUTTON18, OnButton18)

……

//}}AFX_MSG_MAP

END_MESSAGE_MAP()

然后实现OnButton18()函数:

void Records::OnButton18()

{

……

}

7 添加自定义消息

MFC允许用户自己定义消息,自定义消息必须在WM_USER的基础上定义,且自定消息的消息映射使用ON_MESSAGE。

7.1. 定义自定义消息

#define WM_MYMESSAGE (WM_USER + 1)

7.2. 声明自定义消息处理函数

void CMymsgDlg::OnMyFunction();

//可以有参数WPARAM wParam, LPARAM lParam

7.3. 实现自定义消息处理函数

void CMymsgDlg::OnMyFunction()

{

ShellExecute(m_hWnd,"open", "https://www.baidu.com/", NULL, NULL, SW_SHOW);

}

这是一个普通成员函数,非消息响应函数,什么时候有机会执行呢?被消息响应函数调用,或者在消息响应函数中发送自定义消息,通过消息映射得以执行。

7.4. 用宏实现消息映射

BEGIN_MESSAGE_MAP(CMymsg2Dlg, CDialog)

……

ON_MESSAGE(WM_MYMESSAGE, OnMyMessage)

END_MESSAGE_MAP()

7.5. 发送自定义消息到消息队列

可以在一切普通成员函数、消息响应函数,重写的事件响应函数中发送自定义消息,如OnInitDialog()、OnPaint()等。

void CMymsgDlg::OnButton1()

{

PostMessage(WM_MYMESSAGE); //将自定义消息插入消息队列并返回

SendMessage(WM_MYMESSAGE); //将自定义消息插入消息队列并返回

}

或者:

//可以重写OnKeyUp()等键盘、鼠标事件中发出消息

void CMymsgDlg::OnKeyUp(UINT nChar, UINT nRepCnt, UINT nFlags)

{

HWND hWnd = GetSafeHwnd(); //接收消息的窗口句柄

if(nChar == VK_UP) //光标上移键

{

::PostMessage(hWnd, WM_MYMESSAGE, 0, 0); //将自定义消息插入消息队列并返回

return ;

}

CDialog::OnKeyUp(nChar, nRepCnt, nFlags);

}

-End-

本页共126段,5994个字符,14108 Byte(字节)